#include "AS3Model.h"
#include "swf/SWFTag.h"
#include "swf/tags/TagDoABC.h"

AS3SWF::AS3SWF(SWF* swf, const QFileInfo& fileInfo)
	: SWFWrapper(swf)
	, _swf(swf)
	, _isAnalyze(false)
	, _swfFileInfo(fileInfo)
{
	this->analyze();
}

void AS3SWF::getAllABCFiles(std::vector<AS3ABCFile*>& all)
{
	all.insert(all.end(), _abcVec.begin(), _abcVec.end());
}

void AS3SWF::analyze()
{
	if (_isAnalyze) return;
	_isAnalyze = true;

	std::vector<SWFFrame*> allFrames;
	_swf->getAllFrames(allFrames);
	for (SWFFrame* frame : allFrames)
	{
		std::vector<TagDoABC*> allDoABCTags;
		frame->getAllDoABCTags(allDoABCTags);
		for (TagDoABC* tagABC : allDoABCTags)
		{
			AS3ABCFile* abc = new AS3ABCFile(tagABC);
			abc->setSWF(this);
			abc->analyze();
			_abcVec.push_back(abc);
		}
	}
}

void AS3SWFObject::setSWF( AS3SWF* swf )
{
	as3SWF = swf;
}

AS3SWFObject::AS3SWFObject()
	: as3SWF(nullptr)
{

}

void AS3SWFObject::analyze()
{

}


AS3Cpool* AS3ABCFile::getCpool() const
{
	return _pool;
}

size_t AS3ABCFile::getClassCount() const
{
	return _allClassesVec.size();
}

void AS3ABCFile::getAllClasses( std::vector<AS3Class*>& all )
{
	all.insert(all.end(), _allClassesVec.begin(), _allClassesVec.end());
}

AS3ABCFile::AS3ABCFile(TagDoABC* tagABC)
	: _abcFile(tagABC->abcFile)
	, _tag(tagABC)
	, _pool(nullptr)
{
}

AS3ABCFile::~AS3ABCFile()
{
	if (_pool)
		delete _pool;
}

void AS3ABCFile::analyze()
{
	_pool = new AS3Cpool(_abcFile->poolInfo);
	_pool->setABCFile(this);
	_pool->setSWF(as3SWF);
	_pool->analyze();

	size_t idx = 0;
	for (InstanceInfo* ii : _abcFile->instanceInfoVec)
	{
		AS3Class* clazz = new AS3Class(_abcFile->classInfoVec[idx], ii);
		clazz->setABCFile(this);
		clazz->setSWF(as3SWF);
		clazz->analyze();
		_allClassesVec.push_back(clazz);
		++idx;
	}
}

CpoolInfo* AS3ABCFile::getRawCpool() const
{
	return _abcFile->poolInfo;
}

ABCFile* AS3ABCFile::getRawABCFile() const
{
	return _abcFile;
}

std::string AS3ABCFile::getName() const
{
	return _tag->name;
}

void AS3ABCFileObject::setABCFile( AS3ABCFile* file )
{
	abcFile = file;
}

AS3ABCFileObject::AS3ABCFileObject()
	: abcFile(nullptr)
{

}

size_t AS3Class::getMethodsCount() const
{
	return _methodsVec.size();
}

void AS3Class::getAllMethods( std::vector<AS3Method*>& all )
{
	all.insert(all.end(), _methodsVec.begin(), _methodsVec.end());
}

size_t AS3Class::getSlotsCount() const
{
	return _slotsVec.size();
}

void AS3Class::getAllSlots( std::vector<AS3Slot*>& all )
{
	all.insert(all.end(), _slotsVec.begin(), _slotsVec.end());
}

AS3Method* AS3Class::getCinitMethod() const
{
	return _cinitMethod;
}

AS3Method* AS3Class::getConstructor() const
{
	return _iinitMethod;
}

bool AS3Class::isInterface() const
{
	return (_instanceInfo->flags & CONSTANT_ClassInterface) == CONSTANT_ClassInterface;
}

bool AS3Class::isFinal() const
{
	return (_instanceInfo->flags & CONSTANT_ClassFinal) == CONSTANT_ClassFinal; 
}

size_t AS3Class::getInterfacesCount() const
{
	return 0;
}

void AS3Class::getAllInterfaces( std::vector<AS3Class*>& all )
{

}

AS3Type* AS3Class::getSuperClass() const
{
	return nullptr;
}

AS3Class::AS3Class( ClassInfo* ci, InstanceInfo* ii )
	: _instanceInfo(ii)
	, _classInfo(ci)
	, _cinitMethod(nullptr)
	, _iinitMethod(nullptr)
{

}

void AS3Class::analyze()
{
	//package name
	QName* mn = (QName*) abcFile->getRawCpool()->getMultinameInfo(_instanceInfo->name);
	std::string name = mn->toString();
	NamespaceInfo* nsInfo = abcFile->getRawCpool()->getNamespaceInfo(mn->ns);
	
	if (nsInfo->kind == NamespaceKind::PackageNamespace)
	{
		_package = abcFile->getRawCpool()->getString(nsInfo->name);

		_name = _package + "." + mn->toString();
	}
	else
		_name = mn->toString();

	//Traits
	std::vector<TraitInfo*> allTraits = _instanceInfo->traitInfoVec;
	allTraits.insert(allTraits.begin(), _classInfo->traitInfoVec.begin(), _classInfo->traitInfoVec.end());

	ABCFile* rawABC = abcFile->getRawABCFile();
	//Class init method
	_cinitMethod = new AS3Method(
		rawABC->methodInfoVec[_classInfo->cinit],
		rawABC->getMethodBodyInfo(_classInfo->cinit));
	_cinitMethod->setABCFile(abcFile);
	_cinitMethod->setClass(this);
	_cinitMethod->setSWF(as3SWF);
	_cinitMethod->analyze();

	//Constructor
	_iinitMethod = new AS3Method(
		rawABC->methodInfoVec[_instanceInfo->iinit],
		rawABC->getMethodBodyInfo(_instanceInfo->iinit));
	_iinitMethod->setABCFile(abcFile);
	_iinitMethod->setClass(this);
	_iinitMethod->setSWF(as3SWF);
	_iinitMethod->analyze();

	//slot, method, getter, setter ...
	for (TraitInfo* ti : allTraits)
	{
		switch (ti->kind)
		{
		case TraitKind::Slot:
		case TraitKind::Const:
			{
				AS3Slot* slot = new AS3Slot((TraitSlot*) ti);
				slot->setABCFile(abcFile);
				slot->setClass(this);
				slot->setSWF(as3SWF);
				slot->analyze();
				_slotsVec.push_back(slot);
			}
			break;
		case TraitKind::Method:
		case TraitKind::Getter:
		case TraitKind::Setter:
			{
				AS3Method* method = new AS3Method((TraitMethod*) ti);
				method->setABCFile(abcFile);
				method->setClass(this);
				method->setSWF(as3SWF);
				method->analyze();
				_methodsVec.push_back(method);
			}
			break;
		case TraitKind::Class:
		case TraitKind::Function:
		default:

			break;
		}
	}
}

std::string AS3Class::getShortName() const
{
	return _name;
}

std::string AS3Class::getPackageName() const
{
	return _package;
}

void AS3ClassObject::setClass( AS3Class* clazz )
{
	this->clazz = clazz;
}

AS3ClassObject::AS3ClassObject()
	: clazz(nullptr)
{

}

bool AS3ClassMember::isPublic() const
{
	if (ns)
		return ns->kind == NamespaceKind::PackageNamespace;
	return false;
}

bool AS3ClassMember::isPrivate() const
{
	if (ns)
		return ns->kind == NamespaceKind::PrivateNs;
	return false;
}

bool AS3ClassMember::isProtected() const
{
	if (ns)
		return ns->kind == NamespaceKind::ProtectedNamespace;
	return false;
}

bool AS3ClassMember::isStatic() const
{
	return false;
}

void AS3ClassMember::getAllMetadata( std::vector<AS3Metadata*>& all )
{
	all.insert(all.end(), metadatasVec.begin(), metadatasVec.end());
}

AS3ClassMember::AS3ClassMember( TraitInfo* ti )
	: trait(ti)
	, qName(nullptr)
	, ns(nullptr)
{

}

bool AS3ClassMember::hasMetadata() const
{
	return metadatasVec.size() > 0;
}

void AS3ClassMember::analyze()
{
	if (trait == nullptr)
		return;

	CpoolInfo* rawPool = abcFile->getRawCpool();
	qName = (QName*) rawPool->getMultinameInfo(trait->name);
	ns = rawPool->getNamespaceInfo(qName->ns);
	for (UI30 metadataIdx : trait->metadataVec)
	{
		AS3Metadata* metadata = new AS3Metadata(metadataIdx);
		metadata->setABCFile(abcFile);
		metadata->setSWF(as3SWF);
		metadata->analyze();

		metadatasVec.push_back(metadata);
	}
}

std::string AS3ClassMember::getShortName() const
{
	CpoolInfo* rawPool = abcFile->getRawCpool();
	QName* qName = (QName*) rawPool->getMultinameInfo(trait->name);
	return qName->toString();
}

bool AS3Slot::isConst() const
{
	return trait->kind == TraitKind::Const;
}

AS3Slot::AS3Slot( TraitSlot* ti )
	: AS3ClassMember(ti)
	, _traitSlot(ti)
{

}

void AS3Slot::analyze()
{
	AS3ClassMember::analyze();

}

bool AS3Method::isConstruct() const
{
	return false;
}

bool AS3Method::isGetter() const
{
	return trait->kind == TraitKind::Getter;
}

bool AS3Method::isSetter() const
{
	return trait->kind == TraitKind::Setter;
}

bool AS3Method::isOverride() const
{
	return (trait->attr & ATTR_Override) == ATTR_Override;
}

bool AS3Method::isFinal() const
{
	return (trait->attr & ATTR_Final) == ATTR_Final;
}

size_t AS3Method::getParamCount() const
{
	return 0;
}

AS3MethodParam* AS3Method::getParam( unsigned int index ) const
{
	return nullptr;
}

void AS3Method::getAllParams( std::vector<AS3MethodParam*>& all ) const
{

}

AS3Type* AS3Method::getReturnType() const
{
	return nullptr;
}

AS3MethodBody* AS3Method::getBody() const
{
	return _body;
}

AS3Method::AS3Method( TraitMethod* ti )
	: AS3ClassMember(ti)
	, _traitMethod(ti)
	, _body(nullptr)
	, _methodInfo(nullptr)
{

}

AS3Method::AS3Method( MethodInfo* mi, MethodBodyInfo* bodyInfo )
	: AS3ClassMember(nullptr)
	, _body(nullptr)
	, _traitMethod(nullptr)
	, _methodInfo(mi)
	, _methodBodyInfo(bodyInfo)
{

}

void AS3Method::analyze()
{
	AS3ClassMember::analyze();

	ABCFile* rawABC = abcFile->getRawABCFile();
	if (_methodInfo == nullptr)
	{
		_methodInfo = rawABC->methodInfoVec[_traitMethod->methodIndex];
		_methodBodyInfo = rawABC->getMethodBodyInfo(_traitMethod->methodIndex);
	}

	if (_methodBodyInfo != nullptr)
	{
		_body = new AS3MethodBody(_methodBodyInfo);
		_body->setSWF(as3SWF);
		_body->setClass(clazz);
		_body->setABCFile(abcFile);
		_body->analyze();
	}
}

AS3Cpool::AS3Cpool( CpoolInfo* pool )
	: _poolInfo(pool)
{

}

AS3Cpool::~AS3Cpool()
{
	
}

void AS3Metadata::analyze()
{

}

void AS3MethodBody::analyze()
{
	length = _bodyInfo->codeLength;
	data = _bodyInfo->code;
}

AS3MethodBody::AS3MethodBody( MethodBodyInfo* body )
	: _bodyInfo(body)
{

}
